package cn.wuyi.common.config;

import org.apache.ibatis.session.AutoMappingBehavior;
import org.apache.ibatis.session.ExecutorType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;

import cn.wuyi.common.persistence.DynamicDataSource;
import tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
		MapperAutoConfiguration.class,MybatisAutoConfiguration.class,
		MybatisPlusAutoConfiguration.class})
public class SpringMybatisConfig {
    private String typeAliasesPackage = "cn.wuyi";
    private static final int TX_METHOD_TIMEOUT = 300;

    private static final String AOP_POINTCUT_EXPRESSION = "execution(* cn.wuyi..*ServiceImpl.*(..)) ";
    @Autowired
    ConfigurableApplicationContext applicationContext;
    @Bean 
    public DynamicDataSource dataSource(ApplicationContext contex) {
    	DynamicDataSource dataSource=new DynamicDataSource();
    	dataSource.registerDataSource(contex);
    	
        return dataSource;
    }
    @Bean
    public static MapperScannerConfigurer tkMapperScannerConfigurer() {
        tk.mybatis.spring.mapper.MapperScannerConfigurer mapperScannerConfigurer = new tk.mybatis.spring.mapper.MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("cn.wuyi.*.mapper");
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");

		Properties properties = new Properties();
		
		properties.setProperty("mappers","cn.wuyi.common.persistence.CommonBaseMapper"); 
		properties.setProperty("notEmpty", "false");

		properties.setProperty("IDENTITY", "MYSQL");
		mapperScannerConfigurer.setProperties(properties);
        return mapperScannerConfigurer; 
    }
   
    @Bean(name = "sqlSessionFactory")
    @DependsOn("dataSource")
    public SqlSessionFactoryBean sqlSessionFactory(DynamicDataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dataSource);
        sqlSessionFactory.setTypeAliasesSuperType(cn.wuyi.common.pojo.entity.BaseEntity.class);
        sqlSessionFactory.setTypeAliasesPackage(typeAliasesPackage); //扫描bean
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String packageSearchPath = PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "mapper/*Mapper.xml";
        sqlSessionFactory.setMapperLocations(resolver.getResources(packageSearchPath));
        Properties sqlSessionFactoryProperties = new Properties();
        sqlSessionFactoryProperties.put("cacheEnabled", true);
        sqlSessionFactoryProperties.put("lazyLoadingEnabled", true);
        sqlSessionFactoryProperties.put("aggressiveLazyLoading", false);
        sqlSessionFactoryProperties.put("multipleResultSetsEnabled", true);
        sqlSessionFactoryProperties.put("useColumnLabel", true);
        sqlSessionFactoryProperties.put("useGeneratedKeys", true);
        sqlSessionFactoryProperties.put("autoMappingBehavior", AutoMappingBehavior.FULL);
        sqlSessionFactoryProperties.put("defaultExecutorType", ExecutorType.BATCH);
        sqlSessionFactoryProperties.put("defaultStatementTimeout", 25000);
        sqlSessionFactoryProperties.put("mapUnderscoreToCamelCase", false);
        sqlSessionFactory.setConfigurationProperties(sqlSessionFactoryProperties);
//        sqlSessionFactory.setPlugins(new  Interceptor[]{ PaginationInterceptor});
        return sqlSessionFactory;
    }
    
    
    @Bean(name = "txAdvice")
    public TransactionInterceptor txAdvice() {

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        // 只读事务，不做更新操作
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        // 当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("add*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("create*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("batch*", requiredTx);
        txMap.put("modify*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("remove*", requiredTx);
        txMap.put("exec*", requiredTx);
        txMap.put("set*", requiredTx);
        txMap.put("do*", requiredTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("query*", readOnlyTx);
        txMap.put("find*", readOnlyTx);
        txMap.put("*", requiredTx);
        source.setNameMap(txMap);
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager(), source);
        return txAdvice;
    }

    @Bean
    public Advisor txAdviceAdvisor(TransactionInterceptor txAdvice) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice);
    }
    /**自定义 事务管理器 管理我们自定义的 DynamicDataSource 数据源
     * @return
     */
    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(applicationContext.getBean(DynamicDataSource.class));
        return transactionManager;
    }
}
 
